package edu.fiu.accesslog.statistics;

import edu.fiu.accesslog.SingleAccessLog;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import net.firefang.ip2c.Country;
import net.firefang.ip2c.IP2Country;

/**
 * Value Object to hold the stats content between calculation & showing charts.
 *
 * @author Hasan Mahmud
 * @date May 26, 2012
 */
public class LogStatistics
{

      private int timeFrame = 10; //in Minute
      
      
      private String url;
      private double numOfAccess = 0;
      private double numOfUniqueAccess = 0;
      
      private double min = 0;
      private String minDate = null;
      private double max = 0;
      private String maxDate = null;
      private double totalTimeTaken = 0;
      private String startDate = null;
      private String endDate = null;
            
      private  UserAgentStatistics uAgentStatistics;
      private  SearchEngineStatistics referrerStatistics;
              
      private final String weekDays[] = new String[]
      {
            "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
      };
      
      private HashMap<String, Info> countryActivityRecord;
      private HashMap<String, Info> hourlyActivityRecord;
      private HashMap<String, Info> timeFrameActivityRecord;
      private HashMap<String, Info> dailyActivityRecord;
      private HashMap<String, Info> uniqueVisitsActivityRecord;
      private HashMap<String, Info> httpStatusActivityRecord;
      
//      int caching1 = IP2Country.NO_CACHE;  // Straight on file, Fastest startup, slowest queries
//      int caching2 = IP2Country.MEMORY_MAPPED; // Memory mapped file, fast startup, fast quries.
//      int caching3 = IP2Country.MEMORY_CACHE; // load file into memory, slowerst startup, fastest queries
      IP2Country ip2c;

      
      private static LogStatistics singletonInstance = null;
      public static synchronized LogStatistics getInstance()
      {
            if(singletonInstance == null)
            {
                  singletonInstance = new LogStatistics();
            }
                  
            return  singletonInstance;     
      }
      
      private LogStatistics()
      {
            countryActivityRecord = new HashMap<String, Info> (200);
            hourlyActivityRecord = new HashMap<String, Info> (24);
            timeFrameActivityRecord = new HashMap<String, Info> (24);
            dailyActivityRecord = new HashMap<String, Info> (7);
            uniqueVisitsActivityRecord = new HashMap<String, Info> (2000);
            httpStatusActivityRecord = new HashMap<String, Info> (20);
            try
            {
                  ip2c = new IP2Country(IP2Country.MEMORY_CACHE);
            }
            catch (IOException ex)
            {
                  ex.printStackTrace();
            }
            
            uAgentStatistics = UserAgentStatistics.getInstance();
            referrerStatistics = SearchEngineStatistics.getInstance();
           
      }

      public synchronized void addAccessLogEntry(SingleAccessLog sALog)
      {
            numOfAccess++;
            boolean isUnique= false;

            if( ! uniqueVisitsActivityRecord.containsKey(sALog.getRemoteHostName()))
            {
                    isUnique = true;
                    numOfUniqueAccess++;
            }
             addUniqueVisitStatistics(sALog.getRemoteHostName());           
            addCountryStatistics(sALog.getRemoteHostName(), isUnique);

            addPerFrameHourlyDailyStatistics(sALog.getDateTime(), isUnique);
            addHTTPStatusActivityRecord(sALog.getHttpStatusCode(), isUnique);
            
            uAgentStatistics.addAccessLogEntry(sALog, isUnique);
            referrerStatistics.addAccessLogEntry(sALog.getReferer(), isUnique);


      }
      
      private void addHTTPStatusActivityRecord(String statusCode, boolean isUnique)
      {
              RecordUtility.addInHashMap(httpStatusActivityRecord, statusCode, isUnique);
      }
      private void addUniqueVisitStatistics(String ip)
      {
              //third parameter does not matter here
              RecordUtility.addInHashMap(uniqueVisitsActivityRecord, ip, false);
              //System.out.println(uniqueVisitsActivityRecord.size());
      }
      private SimpleDateFormat dayOfWeek = new SimpleDateFormat("EEEE");
      Calendar genCal = Calendar.getInstance();
      
      private void addPerFrameHourlyDailyStatistics(Date accessDate, boolean  isUnique)
      {
            RecordUtility.addInHashMap(dailyActivityRecord, dayOfWeek.format(accessDate), isUnique);
            genCal.setTime(accessDate);
            
            int hour = genCal.get(Calendar.HOUR_OF_DAY);
            int minute = genCal.get(Calendar.MINUTE);
            if( minute > 30 )
            {
                  hour ++;
                  hour = hour % 24;
            }

            RecordUtility.addInHashMap(hourlyActivityRecord, Integer.toString(hour), isUnique);
            
            
            int slotNo = (hour *60+minute)/timeFrame;
            RecordUtility.addInHashMap(timeFrameActivityRecord, Integer.toString(slotNo), isUnique);
            
      }
      

      private void addCountryStatistics(String ip, boolean isUnique)
      {
                   RecordUtility.addInHashMap(countryActivityRecord, getCountryName(ip), isUnique);
      }
      private String getCountryName(String ip)
      {
            String country = "UNKNOWN";
            try
            {
                  //System.out.println(ip);
                  Country ctry = ip2c.getCountry(ip);
                  if (ctry != null)
                  {
                        country = ctry.getName();
                  }

            }
            catch (Exception ex)
            {
                  country = "Faulty Record";
                  //System.out.println("********* Faulty Record: IP:- "+ip);
                  //ex.printStackTrace();
            }
            
            return country;


      }


      public double getNumOfTrans()
      {
            return numOfAccess;
      }

      public void setNumOfTrans(double numOfTrans)
      {
            this.numOfAccess = numOfTrans;
      }

      public String getUrl()
      {
            return url;
      }

      public void setUrl(String url)
      {
            this.url = url;
      }

      public double getMin()
      {
            return min;
      }

      public void setMin(double min)
      {
            this.min = min;
      }

      public double getMax()
      {
            return max;
      }

      public void setMax(double max)
      {
            this.max = max;
      }

      public double getTotalTimeTaken()
      {
            return totalTimeTaken;
      }

      public void setTotalTimeTaken(double totalTimeTaken)
      {
            this.totalTimeTaken = totalTimeTaken;
      }

      public double getAvg()
      {
            if (numOfAccess > 0)
            {
                  return this.totalTimeTaken / numOfAccess;
            }
            else
            {
                  return 0;
            }
      }

      public String getStartDate()
      {
            return startDate;
      }

      public void setStartDate(String startDate)
      {
            this.startDate = startDate;
      }

      public String getEndDate()
      {
            return endDate;
      }

      public void setEndDate(String endDate)
      {
            this.endDate = endDate;
      }

      @Override
      public String toString()
      {
            StringBuilder toString = new StringBuilder();
            toString.append("###############################################################################");
            toString.append("\n# ").append(url);
            toString.append("\n###############################################################################\n");
            toString.append("First Transaction       : ").append(startDate).append("\n");
            toString.append("Last Transaction        : ").append(endDate).append("\n");
            toString.append("Number of Transaction   : ").append(numOfAccess).append("\n");
            toString.append("Total Timetaken(sec.ms) : ").append(totalTimeTaken).append("\n");
            toString.append("Minimum time(sec.ms)    : ").append(min).append(" ").append(minDate).append("\n");
            toString.append("Maximum time(sec.ms)    : ").append(max).append(" ").append(maxDate).append("\n");
            toString.append("Average time(sec.ms)    : ").append(getAvg()).append("\n");
            toString.append("###############################################################################\n\n");
            return toString.toString();
      }
      
      public static final String USA_IP = "64.69.66.192";      
      public ArrayList<Info> getForeignCountryActivityRecord()
      {
            ArrayList<Info> foreignCAR = getCountryActivityRecord();
            String usaName = getCountryName(USA_IP);
            
            for(Info item:foreignCAR)
            {
                  if(item.keyName.equalsIgnoreCase(usaName))
                  {
                        foreignCAR.remove(item);
                        break;
                  }
            }

            return foreignCAR;
      }
      
      public ArrayList<Info> getCountryActivityRecord()
      {

            ArrayList<Info> cARListClone = RecordUtility.getClonedArrayList(countryActivityRecord);
            //Collections.sort(cARListClone, new InfoComparatorByTotalHit(false));
                        
            return cARListClone;
      }
      
      
      public ArrayList<Info> getHourlyActivityRecord()
      {
              ArrayList<Info> hARListClone =  RecordUtility.getClonedArrayList(hourlyActivityRecord);
            Collections.sort(hARListClone, new InfoComparatorKeyToDouble(true));
            return hARListClone;

      }
      
      public ArrayList<Info> getTimeFrameActivityRecord()
      {
              ArrayList<Info> tpARListClone =  RecordUtility.getClonedArrayList(timeFrameActivityRecord);
            Collections.sort(tpARListClone, new InfoComparatorKeyToDouble(true));
            return tpARListClone;

      }
  
       public ArrayList<Info> getDailyActivityRecord()
      {
              ArrayList<Info> dARListClone = new ArrayList<Info>(weekDays.length);
              Info temp ;
             for (String weekDay : weekDays)
            {
                    if(dailyActivityRecord.containsKey(weekDay))
                    {
                            temp = dailyActivityRecord.get(weekDay).clone();
                            dARListClone.add(temp);
                            
                    }

            }
            return dARListClone;

      }
       
        public ArrayList<Info> getFrequentVisitors()
        {
                ArrayList<Info> fVARListClone = RecordUtility.getClonedArrayList(uniqueVisitsActivityRecord);
                Collections.sort(fVARListClone, new InfoComparatorByTotalHit(false));

                int limit = 8;
                for (int i = limit; limit < fVARListClone.size();)
                {
                        fVARListClone.remove(i);
                }
                
                Info totalHitInfo = new Info("Total Hit", numOfAccess , numOfUniqueAccess);
                
                fVARListClone.add(0, totalHitInfo);
                return fVARListClone;
        }


}
